package com.tistory.commons.utils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class SecurityUtil
{
    private final static long time_gap  = 60*60*1000 * 60 * 24; // 10분
    private final static int encLevel   = 2;

    private static byte[] tdesKeyData = {
        (byte)0xFF, (byte)0x34, (byte)0xA7, (byte)0x60, (byte)0xC8,
        (byte)0x14, (byte)0xEA, (byte)0x87, (byte)0x5D, (byte)0x9E,
        (byte)0x74, (byte)0xEE, (byte)0xD1, (byte)0x5B, (byte)0x71,
        (byte)0x9F, (byte)0xA0, (byte)0xC2, (byte)0x59, (byte)0x47,
        (byte)0x67, (byte)0x27, (byte)0x39, (byte)0x74 };

    private static byte[] myIV = {
        (byte)220, (byte)407, (byte)38, (byte)75,
        (byte)82, (byte)34, (byte)87, (byte)204};

    public static String EnCode(String data) throws Exception
    {
        String date = getShortTimeStampString();
        MakeCode    mkcd    = new MakeCode();
        String  rtn = mkcd.EnCode(EnCode(data, date)+date,encLevel);
        mkcd    = null;
        return rtn;
    }

    private static String EnCode(String data, String date) throws Exception
    {
        if(data == null || data.length() == 0)
        {
            return "";
        }

        Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        SecretKeySpec myKey = new SecretKeySpec(fixKey(tdesKeyData,date), "DESede");
        IvParameterSpec ivspec = new IvParameterSpec(myIV);

        c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
        byte[] inputBytes1 = data.getBytes("UTF8");
        byte[] outputBytes1 = c3des.doFinal(inputBytes1);

        sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        return encoder.encode(outputBytes1);
    }
    
    public static String EnCodeFortune(String data, String date) throws Exception
    {
        if(data == null || data.length() == 0)
        {
            return "";
        }

        Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        SecretKeySpec myKey = new SecretKeySpec(fixKey(tdesKeyData,date), "DESede");
        IvParameterSpec ivspec = new IvParameterSpec(myIV);

        c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
        byte[] inputBytes1 = data.getBytes("UTF8");
        byte[] outputBytes1 = c3des.doFinal(inputBytes1);

        sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        return encoder.encode(outputBytes1);
    }

    public static String DeCode(String data, boolean flg) throws Exception
    {
        String  rtn = "";

        try
        {
            MakeCode    mkcd    = new MakeCode();
            data    = mkcd.DeCode(data, encLevel);
            mkcd    = null;

            // 시간정보 이상의 문자열 길이가 넘어오지 않은경우
            if(data.length()<18)
            {
                rtn = "ERROR:암호화 문자열 길이가 잘못 되었습니다.";
            }
            else
            {
                try
                {
                    /* 암호화 되서 넘겨 받은값 */
                    String  sCode   = data.substring(0,data.length()-17);
                    /* 암호화 장비의 시스템 시간 */
                    String  sEnDate = data.substring(data.length()-17);
    
                    long nEnDate   = Long.parseLong(sEnDate);
    
                    /* 복호화할 시스템 시간 */
                    String  date    = getShortTimeStampString();
                    long nDeDate   = Long.parseLong(date); 

                    if(flg)
                    {
                        // 서버간의 지정된 시간 이상의 시간차가 나지 않도록 체크한다.
                        if(nDeDate<(nEnDate+time_gap) )
                        {
                            rtn = DeCode(sCode,sEnDate);
                        }
                        else
                        {
                            System.out.println("서버간의 시간차가 있습니다.\n암호화 연동을 위하여 서버간의 시간 설정을 맞춰 주십시오.");
                            rtn = "ERROR:서버간의 유효 시간이 초과 하였습니다.";
                        }
                    }
                    else
                    {
                        rtn = DeCode(sCode,sEnDate);
                    }
                }
                catch(Exception e)
                {
                    System.out.println("시스템 시간정보 처리 오류");
                    e.printStackTrace();
                    rtn = "ERROR:서버간의 시간정보 처리시 오류가 발생 하였습니다.";
                }
            }
        }
        catch(Exception e)
        {
            rtn = "ERROR:형식에 어긋난 암호화 코드 입니다.";
            e.printStackTrace();
        }

        return rtn;
    }

    public static String DeCode(String data) throws Exception
    {
        String  rtn = "";

        try
        {
            MakeCode    mkcd    = new MakeCode();
            data    = mkcd.DeCode(data, encLevel);
            mkcd    = null;

            // 시간정보 이상의 문자열 길이가 넘어오지 않은경우
            if(data.length()<18)
            {
                rtn = "ERROR:암호화 문자열 길이가 잘못 되었습니다.";
            }
            else
            {
                try
                {
                    /* 암호화 되서 넘겨 받은값 */
                    String  sCode   = data.substring(0,data.length()-17);
                    /* 암호화 장비의 시스템 시간 */
                    String  sEnDate = data.substring(data.length()-17);
    
                    long nEnDate   = Long.parseLong(sEnDate);
    
                    /* 복호화할 시스템 시간 */
                    String  date    = getShortTimeStampString();
                    long nDeDate   = Long.parseLong(date); 

                    // 서버간의 지정된 시간 이상의 시간차가 나지 않도록 체크한다.
                    if(nDeDate<(nEnDate+time_gap) )
                    {
                        rtn = DeCode(sCode,sEnDate);
                    }
                    else
                    {
                        System.out.println("서버간의 시간차가 있습니다.\n암호화 연동을 위하여 서버간의 시간 설정을 맞춰 주십시오.");
                        rtn = "ERROR:서버간의 유효 시간이 초과 하였습니다.";
                    }
                }
                catch(Exception e)
                {
                    System.out.println("시스템 시간정보 처리 오류");
                    e.printStackTrace();
                    rtn = "ERROR:서버간의 시간정보 처리시 오류가 발생 하였습니다.";
                }
            }
        }
        catch(Exception e)
        {
            rtn = "ERROR:형식에 어긋난 암호화 코드 입니다.";
            e.printStackTrace();
        }

        return rtn;
    }

    private static String DeCode(String data, String date) throws Exception
    {
        if(data == null || data.length() == 0)
        {
            return "";
        }

        Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        SecretKeySpec myKey = new SecretKeySpec(fixKey(tdesKeyData,date), "DESede");
        IvParameterSpec ivspec = new IvParameterSpec(myIV);

        c3des.init(Cipher.DECRYPT_MODE, myKey, ivspec);
        sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        byte[] inputBytes1 = decoder.decodeBuffer(data);
        byte[] outputBytes2 = c3des.doFinal(inputBytes1);
        return new String(outputBytes2, "UTF8");
    }
    
    public static String DeCodeFortune(String data, String date) throws Exception
    {
        if(data == null || data.length() == 0)
        {
            return "";
        }

        Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        SecretKeySpec myKey = new SecretKeySpec(fixKey(tdesKeyData,date), "DESede");
        IvParameterSpec ivspec = new IvParameterSpec(myIV);

        c3des.init(Cipher.DECRYPT_MODE, myKey, ivspec);
        sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        byte[] inputBytes1 = decoder.decodeBuffer(data);
        byte[] outputBytes2 = c3des.doFinal(inputBytes1);
        return new String(outputBytes2, "UTF8");
    }

    public static byte[] fixKey( byte[] fkey, String date  )
    {
        byte[] sbt  = date.getBytes();

        for( int i=0; i<sbt.length; i++ )
        {
            fkey[i+3] = sbt[i];
        }

        return fkey;
    }

    private static String getShortTimeStampString()
    {
        java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat ("yyyyMMddHHmmssSSS", java.util.Locale.KOREA);
        return formatter.format(new java.util.Date());
    }

    public static void main(String[] args) throws Exception
    {
        String source = "IF12423423409813513qwrtyuiopasdfghjklzxcvbnm1234567890";
System.out.println("원문\t:\t"+source);
System.out.println(getShortTimeStampString());
        String encrypt = EnCode(source, "20100101000000000");
System.out.println("암호화\t:\t" + encrypt);
System.out.println(getShortTimeStampString());
        String decrypt = DeCode(encrypt, "20100101000000000");
System.out.println("복호화\t:\t" + decrypt);
System.out.println(getShortTimeStampString());

//System.out.println("임의값 복호화 시도1\t:\t"+DeCode("1234567"));
System.out.println(getShortTimeStampString());
//System.out.println("임의값 복호화 시도2\t:\t"+DeCode("123456dfeefeeeeeeeeeeeeewafefwefawfawefwefawe154546454564"));
System.out.println(getShortTimeStampString());

    }
}

